package com.mojo.concurrent.practice;

/**
 * 多线程-抢车位练习
 * <pre>
 * 假设车库有3个车位（可以通过boolean[]数组来表示车库）可以停车，写一个程序模拟多个用户开车离开，停车入库的效果。注意：车位有车时不能停车
 * 分析：典型的生产者-消费者模型，使用队列模拟停车位，队列大小为3
 * @author <a href="mailto:sjj@jianzhimao.com">mojo</a>
 * copyright (C), 2013-2023, 广州九尾信息科技有限公司
 * @date 2023/2/21 11:13
 */
public class ParkingWar {
    public static void main(String[] args) {
        Parking parking = new Parking(0);
        // 生产者：开出
        ParkingWarConsumer consumer = new ParkingWarConsumer(parking);
        new Thread(consumer).start();
        new Thread(consumer).start();
        ParkingWarProducer producer = new ParkingWarProducer(parking);
        new Thread(producer).start();
    }

    // 消费者
    static class ParkingWarConsumer implements Runnable {
        final Parking parking;

        public ParkingWarConsumer(Parking parking) {
            this.parking = parking;
        }

        @Override
        public void run() {
            // 使用synchronize实现同步
            while (true) {
                synchronized (Parking.obj) {
                    // 判断车位是否已满
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    while (parking.count == 0) {
                        try {
                            Parking.obj.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    // 抢车位
                    System.out.println("线程" + Thread.currentThread().getName() + "当前驶出第" + parking.count + "号车位");
                    parking.count--;
                    Parking.obj.notifyAll();
                }
            }
        }
    }

    static class ParkingWarProducer implements Runnable {
        final Parking parking;

        public ParkingWarProducer(Parking parking) {
            this.parking = parking;
        }

        @Override
        public void run() {

            // 使用synchronize实现同步
            while (true) {

                synchronized (Parking.obj) {
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    // 判断车位是否已全空
                    while (parking.count == Parking.total) {
                        try {
                            // 等待有车进入
                            Parking.obj.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    parking.count++;
                    // 抢车位
                    System.out.println("线程" + Thread.currentThread().getName() + "当前抢占第" + parking.count + "号车位");
                    Parking.obj.notifyAll();
                }
            }
        }
    }

    static class Parking {
        final static Object obj = new Object();
        static int total = 3;
        int count;

        public Parking(int count) {
            this.count = count;
        }
    }
}
